package app

import (
	"context"
	"github.com/graphql-go/graphql"
	"github.com/graphql-go/handler"
	"log"
	"net/http"
	"ozonTest/internal/api"
	"ozonTest/internal/config"
	storage "ozonTest/internal/dal/cache"
	"ozonTest/internal/dal/postgres"
	commentsDAO "ozonTest/internal/domain/comments/dao"
	cacheCommentRepo "ozonTest/internal/domain/comments/dao/cache"
	commentRepo "ozonTest/internal/domain/comments/dao/postgres"
	commentService "ozonTest/internal/domain/comments/service"
	postsDAO "ozonTest/internal/domain/posts/dao"
	cachePostRepo "ozonTest/internal/domain/posts/dao/cache"
	postRepo "ozonTest/internal/domain/posts/dao/postgres"
	postService "ozonTest/internal/domain/posts/service"
)

type provider struct {
	commentsService commentService.CommentsService
	postsService    postService.PostsService

	commentsRepo commentsDAO.CommentsDAO
	postsRepo    postsDAO.PostDAO

	serverConfig config.ServerConfig
	dbConfig     config.DBConfig

	cacheStorage *storage.Database
}

func newProvider() *provider {
	return &provider{}
}

func (p *provider) ServerConfig() config.ServerConfig {
	if p.serverConfig == nil {
		cfg, err := config.NewServerConfig()
		if err != nil {
			log.Fatal(err)
		}
		p.serverConfig = cfg
	}
	return p.serverConfig
}

func (p *provider) DBConfig() config.DBConfig {
	if p.dbConfig == nil {
		cfg, err := config.NewDbConfig()
		if err != nil {
			log.Fatal(err)
		}
		p.dbConfig = cfg
	}
	return p.dbConfig
}

func (p *provider) CacheStorage() *storage.Database {
	if p.cacheStorage == nil {
		p.cacheStorage = storage.NewDatabase()
	}
	return p.cacheStorage
}

func (p *provider) CommentsDAO() commentsDAO.CommentsDAO {
	if p.commentsRepo == nil {
		if p.DBConfig().GetDBType() == "postgres" {
			conn := postgres.ConnectToDB(p.dbConfig)
			err := postgres.RunMigrations(conn, "internal/dal/postgres/migrations")
			if err != nil {
				log.Fatalf("Failed to run migrations: %v", err)
			}
			p.commentsRepo = commentRepo.NewCommentsRepo(conn)
			log.Println("postgres type enabled")
		} else if p.DBConfig().GetDBType() == "cache" {
			p.commentsRepo = cacheCommentRepo.NewCommentsRepo(p.CacheStorage())
			log.Println("cache type enabled")
		}
	}

	return p.commentsRepo
}

func (p *provider) PostsDAO() postsDAO.PostDAO {
	if p.postsRepo == nil {
		if p.DBConfig().GetDBType() == "postgres" {
			conn := postgres.ConnectToDB(p.dbConfig)
			p.postsRepo = postRepo.NewPostsRepo(conn)
			log.Println("postgres type enabled")
		} else if p.DBConfig().GetDBType() == "cache" {
			p.postsRepo = cachePostRepo.NewPostsRepo(p.CacheStorage())
			log.Println("cache type enabled")
		}
	}

	return p.postsRepo
}

func (p *provider) CommentsService() commentService.CommentsService {
	if p.commentsService == nil {
		p.commentsService = commentService.NewCommentsService(p.CommentsDAO())
	}
	return p.commentsService
}

func (p *provider) PostsService() postService.PostsService {
	if p.postsService == nil {
		p.postsService = postService.NewPostsService(p.PostsDAO(), p.CommentsService())
	}
	return p.postsService
}

func (p *provider) startServer() {
	schema, err := graphql.NewSchema(graphql.SchemaConfig{
		Query:    api.QueryType,
		Mutation: api.MutationType,
	})
	if err != nil {
		log.Fatalf("Failed to create schema, error: %v", err)
	}

	h := handler.New(&handler.Config{
		Schema:     &schema,
		Pretty:     true,
		GraphiQL:   true,
		Playground: true,
	})

	// Добавление сервисов в контекст запроса
	http.Handle("/graphql", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := context.WithValue(r.Context(), "postsService", p.PostsService())
		ctx = context.WithValue(ctx, "commentsService", p.CommentsService())
		r = r.WithContext(ctx)
		h.ServeHTTP(w, r)
	}))

	log.Println("Now server is running on port 8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}
